/*
 * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package com.sun.corba.se.impl.presentation.rmi;

import java.security.AccessController;
import java.security.PrivilegedAction;

import java.lang.reflect.Method;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationTargetException;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;

import java.rmi.Remote;

import javax.rmi.CORBA.Util;

import org.omg.CORBA.portable.ObjectImpl;
import org.omg.CORBA.portable.Delegate;
import org.omg.CORBA.portable.ServantObject;
import org.omg.CORBA.portable.ApplicationException;
import org.omg.CORBA.portable.RemarshalException;

import org.omg.CORBA.SystemException;

import com.sun.corba.se.spi.orb.ORB;

import com.sun.corba.se.pept.transport.ContactInfoList;

import com.sun.corba.se.spi.transport.CorbaContactInfoList;

import com.sun.corba.se.spi.protocol.CorbaClientDelegate;
import com.sun.corba.se.spi.protocol.LocalClientRequestDispatcher;

import com.sun.corba.se.spi.presentation.rmi.IDLNameTranslator;
import com.sun.corba.se.spi.presentation.rmi.DynamicMethodMarshaller;
import com.sun.corba.se.spi.presentation.rmi.PresentationManager;
import com.sun.corba.se.spi.presentation.rmi.StubAdapter;

import com.sun.corba.se.spi.orbutil.proxy.InvocationHandlerFactory;
import com.sun.corba.se.spi.orbutil.proxy.LinkedInvocationHandler;

import com.sun.corba.se.impl.corba.CORBAObjectImpl;

public final class StubInvocationHandlerImpl implements LinkedInvocationHandler {
    private transient PresentationManager.ClassData classData;
    private transient PresentationManager pm;
    private transient org.omg.CORBA.Object stub;
    private transient Proxy self;

    public void setProxy(Proxy self) {
        this.self = self;
    }

    public Proxy getProxy() {
        return self;
    }

    public StubInvocationHandlerImpl(PresentationManager pm,
                                     PresentationManager.ClassData classData, org.omg.CORBA.Object stub) {
        SecurityManager s = System.getSecurityManager();
        if (s != null) {
            s.checkPermission(new DynamicAccessPermission("access"));
        }
        this.classData = classData;
        this.pm = pm;
        this.stub = stub;
    }

    private boolean isLocal() {
        boolean result = false;
        Delegate delegate = StubAdapter.getDelegate(stub);

        if (delegate instanceof CorbaClientDelegate) {
            CorbaClientDelegate cdel = (CorbaClientDelegate) delegate;
            ContactInfoList cil = cdel.getContactInfoList();
            if (cil instanceof CorbaContactInfoList) {
                CorbaContactInfoList ccil = (CorbaContactInfoList) cil;
                LocalClientRequestDispatcher lcrd =
                        ccil.getLocalClientRequestDispatcher();
                result = lcrd.useLocalInvocation(null);
            }
        }

        return result;
    }

    /**
     * Invoke the given method with the args and return the result.
     * This may result in a remote invocation.
     *
     * @param proxy The proxy used for this class (null if not using java.lang.reflect.Proxy)
     */
    public Object invoke(Object proxy, final Method method,
                         Object[] args) throws Throwable {
        String giopMethodName = classData.getIDLNameTranslator().
                getIDLName(method);
        DynamicMethodMarshaller dmm =
                pm.getDynamicMethodMarshaller(method);

        Delegate delegate = null;
        try {
            delegate = StubAdapter.getDelegate(stub);
        } catch (SystemException ex) {
            throw Util.mapSystemException(ex);
        }

        if (!isLocal()) {
            try {
                org.omg.CORBA_2_3.portable.InputStream in = null;
                try {
                    // create request
                    org.omg.CORBA_2_3.portable.OutputStream out =
                            (org.omg.CORBA_2_3.portable.OutputStream)
                                    delegate.request(stub, giopMethodName, true);

                    // marshal arguments
                    dmm.writeArguments(out, args);

                    // finish invocation
                    in = (org.omg.CORBA_2_3.portable.InputStream)
                            delegate.invoke(stub, out);

                    // unmarshal result
                    return dmm.readResult(in);
                } catch (ApplicationException ex) {
                    throw dmm.readException(ex);
                } catch (RemarshalException ex) {
                    return invoke(proxy, method, args);
                } finally {
                    delegate.releaseReply(stub, in);
                }
            } catch (SystemException ex) {
                throw Util.mapSystemException(ex);
            }
        } else {
            // local branch
            ORB orb = (ORB) delegate.orb(stub);
            ServantObject so = delegate.servant_preinvoke(stub, giopMethodName,
                    method.getDeclaringClass());
            if (so == null) {
                return invoke(stub, method, args);
            }
            try {
                Object[] copies = dmm.copyArguments(args, orb);

                if (!method.isAccessible()) {
                    // Make sure that we can invoke a method from a normally
                    // inaccessible package, as this reflective class must always
                    // be able to invoke a non-public method.
                    AccessController.doPrivileged(new PrivilegedAction() {
                        public Object run() {
                            method.setAccessible(true);
                            return null;
                        }
                    });
                }

                Object result = method.invoke(so.servant, copies);

                return dmm.copyResult(result, orb);
            } catch (InvocationTargetException ex) {
                Throwable mex = ex.getCause();
                // mex should never be null, as null cannot be thrown
                Throwable exCopy = (Throwable) Util.copyObject(mex, orb);
                if (dmm.isDeclaredException(exCopy))
                    throw exCopy;
                else
                    throw Util.wrapException(exCopy);
            } catch (Throwable thr) {
                if (thr instanceof ThreadDeath)
                    throw (ThreadDeath) thr;

                // This is not a user thrown exception from the
                // method call, so don't copy it.  This is either
                // an error or a reflective invoke exception.
                throw Util.wrapException(thr);
            } finally {
                delegate.servant_postinvoke(stub, so);
            }
        }
    }
}
